package zone.czh.woi.woim.server.channel.protocol.initializer;

import io.netty.buffer.ByteBuf;
import io.netty.channel.ChannelHandlerContext;
import io.netty.handler.codec.ByteToMessageDecoder;
import lombok.Data;
import lombok.NoArgsConstructor;
import lombok.experimental.Accessors;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import zone.czh.woi.woim.base.channel.protocol.builder.ProtocolBuilder;
import zone.czh.woi.woim.server.channel.protocol.detector.ProtocolDetector;
import zone.czh.woi.woim.server.constant.AttributeKeyConstant;
import zone.czh.woi.woim.server.util.AttributeKeyUtil;

import java.util.HashMap;
import java.util.List;
import java.util.Map;

/**
*@ClassName: ProtocolDetector
*@Description: None
*@author woi
*/
@Data
@Accessors(chain = true)
@NoArgsConstructor
public class ProtocolInitializer extends ByteToMessageDecoder {

    private Logger log = LoggerFactory.getLogger(ProtocolInitializer.class);

    private ProtocolDetector protocolDetector;

    private ProtocolBuilder protocolBuilder;

    private Intent intent;

    public ProtocolInitializer(ProtocolDetector protocolDetector, ProtocolBuilder protocolBuilder){
        this.protocolDetector=protocolDetector;
        this.protocolBuilder=protocolBuilder;
    }

    @Override
    final protected void decode(ChannelHandlerContext ctx, ByteBuf byteBuf, List<Object> list) throws Exception {
        if (protocolDetector==null||protocolBuilder==null){
            ctx.pipeline().remove(this);
            log.warn("Missing detector or builder");
            return;
        }
        int detectRange = intent==null?protocolDetector.getInitialDetectRange():intent.getDetectRange();
        while (byteBuf.readableBytes()>=detectRange && (intent==null||!intent.isFinished())){

            byteBuf.markReaderIndex();
            byte[] buf = new byte[detectRange];
            byteBuf.readBytes(buf);
            intent = protocolDetector.detect(buf,intent);
            if (intent.isResetReaderIndex()){
                byteBuf.resetReaderIndex();
            }
            if (intent.isFinished()){
                log.debug("Detect protocol => {}",intent.getProtocolName());
                protocolBuilder.build(ctx.pipeline(),intent.getProtocolName());
                ctx.pipeline().remove(this);
                if (intent.isProxy()){
                    AttributeKeyUtil.set(ctx.channel(), AttributeKeyConstant.REAL_REMOTE_IP,intent.getRealRemoteIp());
                    AttributeKeyUtil.set(ctx.channel(), AttributeKeyConstant.REAL_REMOTE_PORT,intent.getRealRemotePort());
                }
            }
            detectRange=intent.getDetectRange();
        }

    }

    @Data
    public static class Intent{
        String protocolName=null;
        int detectRange=1;
        boolean proxy=false;
        String realRemoteIp =null;
        int realRemotePort = 0;
        boolean resetReaderIndex=true;
        boolean finished=false;
        Map<String,Object> extras=new HashMap<>();
    }

}
